<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.1.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/blog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/blog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/blog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/blog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/blog/css/main.css">


<link rel="stylesheet" href="/blog/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"coderiding.gitee.io","root":"/blog/","scheme":"Pisces","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="项目大了之后，APP的启动速度越来越慢，第一个是项目的代码多了，第二个是代码之间的引用，APP启动时互相链接的库越来愈多，程序员的代码风格很不同，留下很多没有优化的代码，写法不规范，一系列的问题会导致APP启动变慢，我们来一起看看启动速度的事...">
<meta property="og:type" content="article">
<meta property="og:title" content="APP启动速度">
<meta property="og:url" content="https://coderiding.gitee.io/blog/2020/07/16/APP%E5%90%AF%E5%8A%A8%E9%80%9F%E5%BA%A6/index.html">
<meta property="og:site_name" content="汶的博客">
<meta property="og:description" content="项目大了之后，APP的启动速度越来越慢，第一个是项目的代码多了，第二个是代码之间的引用，APP启动时互相链接的库越来愈多，程序员的代码风格很不同，留下很多没有优化的代码，写法不规范，一系列的问题会导致APP启动变慢，我们来一起看看启动速度的事...">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-07-16T11:57:44.000Z">
<meta property="article:modified_time" content="2020-09-12T09:05:44.938Z">
<meta property="article:author" content="汶">
<meta property="article:tag" content="性能优化">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://coderiding.gitee.io/blog/2020/07/16/APP%E5%90%AF%E5%8A%A8%E9%80%9F%E5%BA%A6/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>APP启动速度 | 汶的博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/blog/atom.xml" title="汶的博客" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/blog/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">汶的博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/blog/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/blog/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/blog/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://coderiding.gitee.io/blog/2020/07/16/APP%E5%90%AF%E5%8A%A8%E9%80%9F%E5%BA%A6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/blog/images/side_image.png">
      <meta itemprop="name" content="汶">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="汶的博客">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          APP启动速度
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2020-07-16 19:57:44" itemprop="dateCreated datePublished" datetime="2020-07-16T19:57:44+08:00">2020-07-16</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2020-09-12 17:05:44" itemprop="dateModified" datetime="2020-09-12T17:05:44+08:00">2020-09-12</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/blog/categories/%E6%94%B9%E5%8F%98%E4%B8%96%E7%95%8C/" itemprop="url" rel="index"><span itemprop="name">改变世界</span></a>
                </span>
            </span>

          
            <div class="post-description">项目大了之后，APP的启动速度越来越慢，第一个是项目的代码多了，第二个是代码之间的引用，APP启动时互相链接的库越来愈多，程序员的代码风格很不同，留下很多没有优化的代码，写法不规范，一系列的问题会导致APP启动变慢，我们来一起看看启动速度的事...</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h2 id="冷启动"><a href="#冷启动" class="headerlink" title="冷启动"></a>冷启动</h2><ul>
<li>冷启动是指,App点击启动前,它的进程不在系统里,需要系统新创建一个进程分配给它  启动的情况。这是一次完整的启动过程。</li>
</ul>
<h2 id="热启动"><a href="#热启动" class="headerlink" title="热启动"></a>热启动</h2><ul>
<li>热启动是指,App在冷启动后用户将App退后台,在App的进程还在系统里的情况  下,用户重新启动进入App的过程,这个过程做的事情非常少。</li>
</ul>
<h2 id="APP启动3个阶段"><a href="#APP启动3个阶段" class="headerlink" title="APP启动3个阶段"></a>APP启动3个阶段</h2><h3 id="main-函数执行前"><a href="#main-函数执行前" class="headerlink" title="main()函数执行前"></a>main()函数执行前</h3><p>在main()函数执行前,系统主要会做下面几件事情:</p>
<ul>
<li>加载可执行文件（App的.o文件的集合）；</li>
<li>加载动态链接库，进行 rebase指针调整和bind 符号绑定；</li>
<li>Objc运行时的初始处理，包括Objc相关类的注册、category注册、selector唯一性检查等；</li>
<li>初始化，包括了执行+load()）方法、attribute((constructor)）修饰的函数的调用、创建C++静态全局变量。</li>
</ul>
<hr>
<p>相应地,这个阶段对于启动速度优化来说,可以做的事情包括</p>
<ul>
<li>减少动态库加载。每个库本身都有依赖关系，苹果公司建议使用更少的动态库，并且建议在使用动态库的数量较多时，尽量将多个动态库进行合并。数量上，苹果公司最多可以支持6个非系统动态库合并为一个。</li>
<li>减少加载启动后不会去使用的类或者方法。</li>
<li>+load（)方法里的内容可以放到首屏渲染完成后再执行，或使用+initialize（)方法替换掉。因为，在一个+load（)方法里，进行运行时方法替换操作会带来4毫秒的消耗。不要小看这4毫秒，积少成多，执行+load（)方法对启动速度的影响会越来越大。</li>
<li>控制C++全局变量的数量。</li>
</ul>
<h3 id="main-函数执行后"><a href="#main-函数执行后" class="headerlink" title="main()函数执行后"></a>main()函数执行后</h3><ul>
<li>main（)函数执行后的阶段，指的是从main（)函数执行开始，到appDelegate的didFinishLaunchingWithOptions 方法里首屏渲染相关方法执行完成。</li>
<li>首页的业务代码都是要在这个阶段，也就是首屏渲染前执行的，主要包括了：</li>
</ul>
<ul>
<li><p>首屏初始化所需配置文件的读写操作;  </p>
</li>
<li><p>首屏列表大数据的读取;  </p>
</li>
<li><p>首屏渲染的大量计算等。</p>
</li>
<li><p>很多时候,开发者会把各种初始化工作都放到这个阶段执行,导致渲染完成滞后。更加优化  的开发方式,应该是从功能上梳理岀哪些是首屏渲染必要的初始化功能,哪些是App启动必  要的初始化功能,而哪些是只需要在对应功能开始使用时才需要初始化的。梳理完之后,将  这些初始化功能分别放到合适的阶段进行。</p>
</li>
</ul>
<h3 id="首屏渲染完成后"><a href="#首屏渲染完成后" class="headerlink" title="首屏渲染完成后"></a>首屏渲染完成后</h3><ul>
<li>首屏渲染后的这个阶段,主要完成的是,非首屏其他业务服务模块的初始化、监听的注册  配置文件的读取等。从函数上来看,这个阶段指的就是截止到did FinishLaunchingWithOptions方法作用域内执行首屏渲染之后的所有方法执行完成。简  单说的话,这个阶段就是从渲染完成时开始,到 didFinishLaunchingWithOptions方法作  用域结束时结束    </li>
<li>这个阶段用户已经能够看到App的首页信息了,所以优化的优先级排在最后。但是,那些会  卡住主线程的方法还是需要最优先处理的,不然还是会影响到用户后面的交互操作   </li>
<li>明白了App启动阶段需要完成的工作后,我们就可以有的放矢地进行启动速度的优化了。这  些优化,包括了功能级别和方法级别的启动优化。接下来,我们就从这两个角度展开看看。</li>
</ul>
<h2 id="功能级别的启动优化"><a href="#功能级别的启动优化" class="headerlink" title="功能级别的启动优化"></a>功能级别的启动优化</h2><ul>
<li>功能级别的启动优化，就是要从main（）函数执行后这个阶段下手。</li>
</ul>
<hr>
<ul>
<li>我想，你所在的团队一定面临过启动阶段的代码功能堆积、无规范、难维护的问题吧。在App项目开发初期，开发人员不多、代码量也没那么大时，这种情况比较少见。但到了后期，App业务规模扩大，团队人员水平参差不齐，各种代码问题就会爆发出来，终归需要来次全面治理。</li>
<li>而全面治理过程中的手段、方法和碰到的问题，对于后面的规范制定以及启动速度监控都有着重要的意义。那么，我们要怎样从功能级别来进行全面的启动优化治理呢？</li>
<li>优化的思路是：main（)函数开始执行后到首屏渲染完成前只处理首屏相关的业务，其他非首屏业务的初始化、监听注册、配置文件读取等都放到首屏渲染完成后去做。如下图所示：</li>
</ul>
<h2 id="方法级别的启动优化"><a href="#方法级别的启动优化" class="headerlink" title="方法级别的启动优化"></a>方法级别的启动优化</h2><ul>
<li>经过功能级别的启动优化,也就是将非首屏业务所需的功能滞后以后,从用户点击App到看  到首屏的时间将会有很大程度的缩短,也就达到了优化App启动速度的目的    </li>
<li>在这之后,我们需要进一步做的,是检查首屏渲染完成前主线程上有哪些耗时方法,将没必  要的耗时方法滞后或者异步执行。通常情况下,耗时较长的方法主要发生在计算大量数据的  情况下,具体的表现就是加载、编辑、存储图片和文件等资源。    </li>
<li>那么,你觉得是不是只需要优化对资源的操作就可以了呢?   </li>
<li>当然不是。就像+load()方法,一个耗时4毫秒,100个就是400毫秒,这种耗时用户也  是能明显感知到的    </li>
<li>比如,我以前使用的 Reactive Cocoa框架(这是一个ios上的响应式编程框架),每创建  一个信号都有6毫秒的耗时。这样,稍不注意各种信号的创建就都被放在了首屏渲染完成  前,进而导致App的启动速度大幅变慢。    </li>
<li>类似这样单个方法耗时不多,但是由于堆积导致App启动速度大幅变慢的方法数不胜数。所  以,你需要一个能够对启动方法耗时进行全面、精确检查的手段。    </li>
<li>那么问题来了,有哪些监控手段?这些监控手段各有什么优缺点?你又该如何选择呢?    </li>
</ul>
<h3 id="目前来看-对App启动速度的监控-主要有两种手段。"><a href="#目前来看-对App启动速度的监控-主要有两种手段。" class="headerlink" title="目前来看,对App启动速度的监控,主要有两种手段。"></a>目前来看,对App启动速度的监控,主要有两种手段。</h3><blockquote>
<p>第一种方法是，定时抓取主线程上的方法调用堆栈，计算一段时间里各个方法的耗时。</p>
</blockquote>
<ul>
<li>Xcode 工具套件里自带的Time Profler，采用的就是这种方式。</li>
<li>这种方式的优点是，开发类似工具成本不高，能够快速开发后集成到你的App中，以便在真实环境中进行检查。</li>
<li>说到定时抓取，就会涉及到定时间隔的长短问题。<ul>
<li>定时间隔设置得长了，会漏掉一些方法，从而导致检查出来的耗时不精确；</li>
<li>而定时间隔设置得短了，抓取堆栈这个方法本身调用过多也会影响整体耗时，导致结果不准确。</li>
</ul>
</li>
<li>这个定时间隔如果小于所有方法执行的时间(比如0.002秒),那么基本就能监控到所有方  法。但这样做的话,整体的耗时时间就不够准确。一般将这个定时间隔设置为0.01秒。这  样设置,对整体耗时的影响小,不过很多方法耗时就不精确了。但因为整体耗时的数据更加  重要些,单个方法耗时精度不高也是可以接受的,所以这个设置也是没问题的。    </li>
<li>总结来说,定时抓取主线程调用栈的方式虽然精准度不够高,但也是够用的。</li>
</ul>
<hr>
<blockquote>
<p>第二种方法是,对objc_ msg Send方法进行hook来掌握所有方法的执行耗时。    </p>
</blockquote>
<ul>
<li>hook方法的意思是,在原方法开始执行时换成执行其他你指定的方法,或者在原有方法执  行前后执行你指定的方法,来达到掌握和改变指定方法的目的    </li>
<li>hook objc_ msgSend这种方式的优点是非常精确,而缺点是只能针对 Objective-C的方  法。当然,对于c方法和bock也不是没有办法,你可以使用lib的币ca来达成  hook,但缺点就是编写维护相关工具门槛高。    </li>
<li>关于,[i相关的内容,我会在后面的第35篇文章”ibfh:动态调用和定义C函数”里和  你详细说明。    </li>
<li>综上,如果对于检查结果精准度要求高的话,我比较推荐你使用 hook objc_msgSend方式  来检查启动方法的执行耗时。</li>
</ul>
<h2 id="如何做一个方法级别启动耗时检查工具来辅助分析和监控"><a href="#如何做一个方法级别启动耗时检查工具来辅助分析和监控" class="headerlink" title="如何做一个方法级别启动耗时检查工具来辅助分析和监控"></a>如何做一个方法级别启动耗时检查工具来辅助分析和监控</h2><ul>
<li>使用 hook objc_msgSend方式来检查启动方法的执行耗时时，我们需要实现一个称手的启动时间检查工具。那么，我们应该如何实现这个工具呢?</li>
<li>现在，我就一步一步地和你说说具体怎么做。</li>
<li>首先，你要了解为什么hook了objc_msgSend方法，就可以hook 全部 Objective-C的方法?</li>
<li>Objective-C里每个对象都会指向一个类，每个类都会有一个方法列表，方法列表里的每个方法都是由selector、函数指针和 metadata组成的。</li>
<li>objc_msgSend方法干的活儿，就是在运行时根据对象和方法的selector去找到对应的函数指针，然后执行。也就是说，objc_msgSend是Objective-C里方法执行的必经之路，能够控制所有的Objective-C的方法。</li>
<li>objc_msgSend 本身是用汇编语言写的，这样做的原因主要有两个：<ul>
<li>一个原因是，objc_msgSend的调用频次最高，在它上面进行的性能优化能够提升整个App 生命周期的性能。而汇编语言在性能优化上属于原子级优化，能够把优化做到极致。所以，这种投入产出比无疑是最大的。</li>
<li>另一个原因是，其他语言难以实现未知参数跳转到任意函数指针的功能。</li>
</ul>
</li>
<li>现在，苹果公司已经开源了Objective-C的运行时代码。你可以在苹果公司的开源网站，找到objc_msgSend的源码。</li>
<li>上图列出的是所有架构的实现,包括x86_64等。objc_ msgSend是ios方式执行最核心  的部分,编程领域的宝藏,值得你深入探究和细细品味。    </li>
<li>objc_ msgSend方法执行的逻辑是:先获取对象对应类的信息,再获取方法的缓存,根据方  法的 selector查找函数指针,经过异常错误处理后,最后跳到对应函数的实现。    </li>
<li>按照这个逻辑去看源码会更加清晰,更容易注意到实现细节。阅读 objc_msgSend源码是编  写方法级耗时工具的一个必要的环节,后面还需要编写一些对应的汇编代码。</li>
</ul>

    </div>

    
    
    

      <footer class="post-footer">
          <div class="post-tags">
              <a href="/blog/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/" rel="tag"># 性能优化</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item"></div>
      <div class="post-nav-item">
    <a href="/blog/2020/08/22/%E5%BC%80%E5%8F%91Awesomes%E7%B3%BB%E5%88%97%E5%90%88%E9%9B%86/" rel="next" title="开发Awesomes系列合集">
      开发Awesomes系列合集 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%86%B7%E5%90%AF%E5%8A%A8"><span class="nav-number">1.</span> <span class="nav-text">冷启动</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E7%83%AD%E5%90%AF%E5%8A%A8"><span class="nav-number">2.</span> <span class="nav-text">热启动</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#APP%E5%90%AF%E5%8A%A83%E4%B8%AA%E9%98%B6%E6%AE%B5"><span class="nav-number">3.</span> <span class="nav-text">APP启动3个阶段</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#main-%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E5%89%8D"><span class="nav-number">3.1.</span> <span class="nav-text">main()函数执行前</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#main-%E5%87%BD%E6%95%B0%E6%89%A7%E8%A1%8C%E5%90%8E"><span class="nav-number">3.2.</span> <span class="nav-text">main()函数执行后</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E9%A6%96%E5%B1%8F%E6%B8%B2%E6%9F%93%E5%AE%8C%E6%88%90%E5%90%8E"><span class="nav-number">3.3.</span> <span class="nav-text">首屏渲染完成后</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%8A%9F%E8%83%BD%E7%BA%A7%E5%88%AB%E7%9A%84%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96"><span class="nav-number">4.</span> <span class="nav-text">功能级别的启动优化</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%96%B9%E6%B3%95%E7%BA%A7%E5%88%AB%E7%9A%84%E5%90%AF%E5%8A%A8%E4%BC%98%E5%8C%96"><span class="nav-number">5.</span> <span class="nav-text">方法级别的启动优化</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E7%9B%AE%E5%89%8D%E6%9D%A5%E7%9C%8B-%E5%AF%B9App%E5%90%AF%E5%8A%A8%E9%80%9F%E5%BA%A6%E7%9A%84%E7%9B%91%E6%8E%A7-%E4%B8%BB%E8%A6%81%E6%9C%89%E4%B8%A4%E7%A7%8D%E6%89%8B%E6%AE%B5%E3%80%82"><span class="nav-number">5.1.</span> <span class="nav-text">目前来看,对App启动速度的监控,主要有两种手段。</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%A6%82%E4%BD%95%E5%81%9A%E4%B8%80%E4%B8%AA%E6%96%B9%E6%B3%95%E7%BA%A7%E5%88%AB%E5%90%AF%E5%8A%A8%E8%80%97%E6%97%B6%E6%A3%80%E6%9F%A5%E5%B7%A5%E5%85%B7%E6%9D%A5%E8%BE%85%E5%8A%A9%E5%88%86%E6%9E%90%E5%92%8C%E7%9B%91%E6%8E%A7"><span class="nav-number">6.</span> <span class="nav-text">如何做一个方法级别启动耗时检查工具来辅助分析和监控</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="汶"
      src="/blog/images/side_image.png">
  <p class="site-author-name" itemprop="name">汶</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/blog/archives/">
        
          <span class="site-state-item-count">6</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">分类</span>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">5</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/codeRiding" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;codeRiding" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://weibo.com/coderiding" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;coderiding" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>Weibo</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">汶</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/blog/lib/anime.min.js"></script>
  <script src="/blog/lib/velocity/velocity.min.js"></script>
  <script src="/blog/lib/velocity/velocity.ui.min.js"></script>

<script src="/blog/js/utils.js"></script>

<script src="/blog/js/motion.js"></script>


<script src="/blog/js/schemes/pisces.js"></script>


<script src="/blog/js/next-boot.js"></script>




  















  

  

</body>
</html>
